package org.ehe.business.workstation.service;

import cn.hutool.core.util.StrUtil;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ehe.business.bom.domain.BomStructureTemp;
import org.ehe.business.bom.service.BomStructureTempService;
import org.ehe.business.workstation.domain.NonStandardPartImport;
import org.ehe.business.workstation.domain.Workstation;
import org.ehe.business.workstation.mapper.WorkstationMapper;
import org.ehe.common.core.exception.ServiceException;
import org.ehe.common.satoken.utils.LoginHelper;
import org.ehe.erp.domain.product.ErpMaterial;
import org.ehe.erp.domain.product.ErpProduct;
import org.ehe.erp.domain.product.ErpProductUnit;
import org.ehe.erp.service.product.IErpProductService;
import org.ehe.erp.service.product.IErpProductUnitService;
import org.ehe.erp.service.product.MaterialService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * 非标件导入服务
 *
 * @author zhangnn
 * @date 2025-10-23
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NonStandardPartImportService {

    private final WorkstationMapper workstationMapper;
    private final WorkstationCodeService codeService;
    private final IErpProductService productService;
    private final MaterialService materialService;
    private final IErpProductUnitService unitService;
    private final BomStructureTempService bomStructureTempService;

    /**
     * 固定分类编码 - 非标件库
     */
    private static final String NON_STANDARD_CATEGORY = "03";

    /**
     * 导入Excel文件
     *
     * @param file     Excel文件
     * @param importId 导入批次ID
     * @return 导入结果
     */
    @Transactional(rollbackFor = Exception.class)
    public ImportResult importExcel(MultipartFile file, Long importId) {
        ImportResult result = new ImportResult();

        try {
            // 预加载字典数据
            Map<String, Workstation> workstationMap = loadWorkstationMap();
            Map<String, String> materialMap = loadMaterialMap();
            Map<String, Long> unitMap = loadUnitMap();

            // 用于缓存产品
            Map<String, ErpProduct> productCache = new HashMap<>();
            List<ErpProduct> newProducts = new ArrayList<>();
            List<BomStructureTemp> bomStructures = new ArrayList<>();

            // 读取Excel（第1行为表头，第2行为说明行，从第3行开始读取数据）
            EasyExcel.read(file.getInputStream(), NonStandardPartImport.class,
                    new NonStandardPartListener(result, workstationMap, materialMap, unitMap,
                            productCache, newProducts, bomStructures, importId, this))
                    .sheet()
                    .headRowNumber(1) // 第1行是表头
                    .doRead();

            // 批量保存新产品
            if (!newProducts.isEmpty()) {
                productService.saveBatch(newProducts);
                log.info("批量保存产品完成，数量：{}", newProducts.size());

                // 更新BOM结构中的产品ID
                updateBomStructureItemIds(bomStructures, productCache, newProducts);
            }

            // 批量保存BOM结构
            if (!bomStructures.isEmpty()) {
                bomStructureTempService.saveBatch(bomStructures);
                log.info("批量保存BOM结构完成，数量：{}", bomStructures.size());
            }

            result.setMessage(String.format("导入完成：总计 %d 行，成功 %d 行，失败 %d 行",
                    result.getTotalRows(), result.getSuccessRows(), result.getFailureRows()));

        } catch (IOException e) {
            log.error("读取Excel文件失败", e);
            throw new ServiceException("读取Excel文件失败：" + e.getMessage());
        }

        return result;
    }

    /**
     * 加载工位映射
     */
    private Map<String, Workstation> loadWorkstationMap() {
        Map<String, Workstation> map = new HashMap<>();
        List<Workstation> list = workstationMapper.selectList(null);

        for (Workstation ws : list) {
            map.put(ws.getCode(), ws);
        }

        log.info("加载工位{}个", map.size());
        return map;
    }

    /**
     * 加载材料映射
     */
    private Map<String, String> loadMaterialMap() {
        Map<String, String> map = new HashMap<>();
        List<ErpMaterial> materials = materialService.list(
                new LambdaQueryWrapper<ErpMaterial>()
                        .eq(ErpMaterial::getDelFlag, 0)
                        .eq(ErpMaterial::getStatus, 1));

        for (ErpMaterial material : materials) {
            String cleanName = cleanString(material.getMaterialName());
            if (StrUtil.isNotBlank(cleanName)) {
                map.put(cleanName, material.getMaterialCode());
            }
        }

        log.info("加载材料{}个", map.size());
        return map;
    }

    /**
     * 加载单位映射
     */
    private Map<String, Long> loadUnitMap() {
        Map<String, Long> map = new HashMap<>();
        List<ErpProductUnit> units = unitService.list(
                new LambdaQueryWrapper<ErpProductUnit>()
                        .eq(ErpProductUnit::getDelFlag, 0)
                        .eq(ErpProductUnit::getStatus, 0));

        for (ErpProductUnit unit : units) {
            String cleanName = cleanString(unit.getName());
            if (StrUtil.isNotBlank(cleanName)) {
                map.put(cleanName, unit.getId());
            }
        }

        log.info("加载单位{}个", map.size());
        return map;
    }

    /**
     * 处理单行数据
     */
    public boolean processRow(NonStandardPartImport row, int rowNum,
            Map<String, Workstation> workstationMap,
            Map<String, String> materialMap,
            Map<String, Long> unitMap,
            Map<String, ErpProduct> productCache,
            List<ErpProduct> newProducts,
            List<BomStructureTemp> bomStructures,
            Long importId,
            List<String> errors) {
        try {
            // 1. 校验工位编号
            String wsCodeInput = cleanString(row.getWorkstationCode());
            if (StrUtil.isBlank(wsCodeInput)) {
                errors.add(String.format("第%d行：工位编号不能为空", rowNum));
                return false;
            }

            // 提取工位编号(格式: "001-工位名称" 或 "001")
            String wsCode = wsCodeInput;
            if (wsCodeInput.contains("-")) {
                wsCode = wsCodeInput.substring(0, wsCodeInput.indexOf("-")).trim();
            }

            if (!wsCode.matches("\\d{3}")) {
                errors.add(String.format("第%d行：工位编号必须为3位数字", rowNum));
                return false;
            }

            Workstation workstation = workstationMap.get(wsCode);
            if (workstation == null) {
                errors.add(String.format("第%d行：工位编号[%s]不存在", rowNum, wsCode));
                return false;
            }

            // 2. 校验必填字段
            String deviceName = cleanString(row.getDeviceName()); // 设备名称改为非必填
            String partName = cleanString(row.getPartName());
            String spec = cleanString(row.getSpecification());
            String unitName = cleanString(row.getUnit());

            if (StrUtil.isBlank(partName)) {
                errors.add(String.format("第%d行：物料名称不能为空", rowNum));
                return false;
            }

            if (StrUtil.isBlank(spec)) {
                errors.add(String.format("第%d行：规格型号不能为空", rowNum));
                return false;
            }

            // 3. 生成产品编码
            // 如果设备名称为空，使用工位名称
            String deviceNameForCode = StrUtil.isNotBlank(deviceName) ? deviceName : workstation.getName();
            WorkstationCodeService.CodeGenerateResult codeResult = codeService.generateCode(wsCode, deviceNameForCode);

            if (!codeResult.isSuccess()) {
                errors.add(String.format("第%d行：生成编码失败 - %s", rowNum, codeResult.getMessage()));
                return false;
            }

            String fullCode = codeResult.getFullCode(); // 如: 03.001.000001

            // 4. 处理材料
            String materialCode = null;
            String materialName = cleanString(row.getMaterial());
            if (StrUtil.isNotBlank(materialName)) {
                materialCode = materialMap.get(materialName);
                if (materialCode == null) {
                    log.warn("第{}行：材料[{}]未找到，将忽略", rowNum, materialName);
                }
            }

            // 5. 处理单位
            Long unitId = 1L; // 默认单位
            if (StrUtil.isNotBlank(unitName)) {
                Long foundUnitId = unitMap.get(unitName);
                if (foundUnitId != null) {
                    unitId = foundUnitId;
                } else {
                    log.warn("第{}行：单位[{}]未找到，使用默认单位", rowNum, unitName);
                }
            }

            // 6. 创建产品
            ErpProduct product = createOrGetProduct(
                    fullCode, wsCode, partName, spec, materialCode, unitId,
                    row.getRemark(), productCache, newProducts);

            // 7. 创建BOM结构
            BomStructureTemp bomStructure = createBomStructure(
                    importId, product, fullCode, partName, spec,
                    String.valueOf(unitId), BigDecimal.ONE, rowNum, row.getRemark());
            bomStructures.add(bomStructure);

            log.info("第{}行：成功处理 - 编码:{}, 名称:{}", rowNum, fullCode, partName);
            return true;

        } catch (Exception e) {
            log.error("处理第{}行数据失败", rowNum, e);
            errors.add(String.format("第%d行：处理异常 - %s", rowNum, e.getMessage()));
            return false;
        }
    }

    /**
     * 创建或获取产品
     */
    private ErpProduct createOrGetProduct(String code, String wsCode, String name, String spec,
            String materialCode, Long unitId, String remark,
            Map<String, ErpProduct> productCache,
            List<ErpProduct> newProducts) {

        // 检查缓存
        String cacheKey = code + "|" + spec;
        ErpProduct cached = productCache.get(cacheKey);
        if (cached != null) {
            return cached;
        }

        // 查询数据库
        ErpProduct existing = productService.getOne(
                new LambdaQueryWrapper<ErpProduct>()
                        .eq(ErpProduct::getBarCode, code)
                        .eq(ErpProduct::getDelFlag, 0));

        if (existing != null) {
            productCache.put(cacheKey, existing);
            return existing;
        }

        // 创建新产品
        ErpProduct product = new ErpProduct();
        product.setName(name);
        product.setBarCode(code);
        product.setCustomizeCode(code);
        product.setCategoryCode(code); // 非标件库
        product.setWorkstationCode(wsCode);
        product.setType(NON_STANDARD_CATEGORY);
        product.setStandard(spec);
        product.setMaterial(materialCode);
        product.setUnitId(unitId);
        product.setRemark(remark);
        product.setStatus(0L);
        product.setDelFlag(0L);
        product.setTenantId(LoginHelper.getTenantId());

        newProducts.add(product);
        productCache.put(cacheKey, product);

        return product;
    }

    /**
     * 创建BOM结构
     */
    private BomStructureTemp createBomStructure(Long importId, ErpProduct product, String itemCode,
            String itemName, String specification, String unit,
            BigDecimal quantity, int seqNo, String remark) {

        BomStructureTemp structure = new BomStructureTemp();
        structure.setImportId(importId);
        structure.setParentItemId(null);
        structure.setItemId(product != null ? product.getId() : null); // 后续赋值
        structure.setItemCode(itemCode);
        structure.setItemName(itemName);
        structure.setItemType(5); // 非标件
        structure.setSpecification(specification);
        structure.setUnit(unit);
        structure.setQuantity(quantity != null ? quantity : BigDecimal.ONE);
        structure.setScrapRate(BigDecimal.ZERO);
        structure.setLevelNo(1);
        structure.setSeqNo(seqNo);
        structure.setIsKeyItem(0);
        structure.setIsOptional(0);
        structure.setRemark(remark);
        structure.setDelFlag("0");
        structure.setTenantId(LoginHelper.getTenantId());

        return structure;
    }

    /**
     * 更新BOM结构中的产品ID
     */
    private void updateBomStructureItemIds(List<BomStructureTemp> bomStructures,
            Map<String, ErpProduct> productCache,
            List<ErpProduct> newProducts) {
        // 更新缓存
        for (ErpProduct product : newProducts) {
            if (product.getId() != null) {
                String key = product.getBarCode() + "|" + product.getStandard();
                productCache.put(key, product);
            }
        }

        // 更新BOM结构中的itemId
        for (BomStructureTemp structure : bomStructures) {
            if (structure.getItemId() == null) {
                String key = structure.getItemCode() + "|" + structure.getSpecification();
                ErpProduct product = productCache.get(key);
                if (product != null && product.getId() != null) {
                    structure.setItemId(product.getId());
                }
            }
        }
    }

    /**
     * 清理字符串
     */
    private String cleanString(String str) {
        if (str == null) {
            return null;
        }
        str = str.trim().replaceAll("\\s+", " ");
        return str.isEmpty() ? null : str;
    }

    /**
     * Excel监听器
     */
    private static class NonStandardPartListener extends AnalysisEventListener<NonStandardPartImport> {

        private final ImportResult result;
        private final Map<String, Workstation> workstationMap;
        private final Map<String, String> materialMap;
        private final Map<String, Long> unitMap;
        private final Map<String, ErpProduct> productCache;
        private final List<ErpProduct> newProducts;
        private final List<BomStructureTemp> bomStructures;
        private final Long importId;
        private final NonStandardPartImportService service;

        private int currentRow = 0;

        public NonStandardPartListener(ImportResult result,
                Map<String, Workstation> workstationMap,
                Map<String, String> materialMap,
                Map<String, Long> unitMap,
                Map<String, ErpProduct> productCache,
                List<ErpProduct> newProducts,
                List<BomStructureTemp> bomStructures,
                Long importId,
                NonStandardPartImportService service) {
            this.result = result;
            this.workstationMap = workstationMap;
            this.materialMap = materialMap;
            this.unitMap = unitMap;
            this.productCache = productCache;
            this.newProducts = newProducts;
            this.bomStructures = bomStructures;
            this.importId = importId;
            this.service = service;
        }

        @Override
        public void invoke(NonStandardPartImport data, AnalysisContext context) {
            currentRow++;

            // 跳过第1行（说明行：请在下方填写非标件信息...）
            if (currentRow == 1) {
                log.debug("跳过说明行");
                return;
            }

            result.incrementTotal();

            boolean success = service.processRow(
                    data, currentRow + 1, workstationMap, materialMap, unitMap,
                    productCache, newProducts, bomStructures, importId, result.getErrors());

            if (success) {
                result.incrementSuccess();
            } else {
                result.incrementFailure();
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            log.info("Excel解析完成，共{}行数据", currentRow);
        }
    }

    /**
     * 导入结果
     */
    public static class ImportResult {
        private int totalRows;
        private int successRows;
        private int failureRows;
        private String message;
        private List<String> errors = new ArrayList<>();

        public void incrementTotal() {
            totalRows++;
        }

        public void incrementSuccess() {
            successRows++;
        }

        public void incrementFailure() {
            failureRows++;
        }

        // Getters and Setters
        public int getTotalRows() {
            return totalRows;
        }

        public int getSuccessRows() {
            return successRows;
        }

        public int getFailureRows() {
            return failureRows;
        }

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }

        public List<String> getErrors() {
            return errors;
        }

        public String getDetailMessage() {
            StringBuilder sb = new StringBuilder(message);
            if (!errors.isEmpty()) {
                sb.append("\n\n错误详情：\n");
                int limit = Math.min(errors.size(), 20);
                for (int i = 0; i < limit; i++) {
                    sb.append(errors.get(i)).append("\n");
                }
                if (errors.size() > limit) {
                    sb.append("...还有").append(errors.size() - limit).append("条错误未显示");
                }
            }
            return sb.toString();
        }
    }
}
